package com.fm.jbsql.sql;

import com.fm.jbsql.util.CmdOpt;
import com.fm.jbsql.util.JbsqlException;
//import com.sybase.jdbcx.EedInfo;

import java.sql.*;
import java.util.logging.Logger;
import java.io.PrintStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;

/**
 * Created by IntelliJ IDEA.
 * User: syaroshenko
 * Date: Jul 13, 2006
 * Time: 11:36:27 AM
 * Base class for SQL-drivers.
 */

/**
 * Base class for SQL-drivers.
 */
public abstract class DbDriver {
    private static final Logger logger = Logger.getLogger("com.fm.jbsql.sql.DbDriver");

    /**
     * Severity state for displaying detailed error message info
     */
    //private static final int SYBASE_DB_BASE_ERR = 11;

    /**
    * Class to handle reserved tokens
    */
    public static final class ETokenType {
        public final static int ETT_NONE =              0,
                                ETT_CODE_BEFORE_START = 1,
                                ETT_CODE_AFTER_START =  2,
                                ETT_CODE_END =          3,
                                ETT_TERMINATOR =        4,
                                ETT_SAVETERMINATOR =    5;
    }

    /**
     */
    public SQLScriptCompiler getCompiler(){
    	return new SQLScriptCompiler();
    }

    public boolean checkForWarning(Statement stmt) {
    	boolean ret=false;
    	try{
	    	ret=this.checkForWarning(stmt.getWarnings());
    	}catch(SQLException e){
    		ret=this.checkForWarning(e);
    	}
    	try{stmt.clearWarnings();}catch(SQLException e){}
    	return ret;
    }
    /**
     * Check SQL-warnings.
     * @param sqe SQL exception
     * DMLU: this method should not be static, and should be customized for driver.
     * @return boolean
     */
    public boolean checkForWarning(SQLException sqe) {
        boolean isFatalError = false;
        while ( sqe != null ) {
            /*if ( sqe instanceof EedInfo ) {
                // Error is using the addtional TDS error data.
                EedInfo eedi = (EedInfo) sqe;
                if ( eedi.getSeverity() >= SYBASE_DB_BASE_ERR ) {
                    boolean firstOnLine = true;
                    logger.severe("Msg " + sqe.getErrorCode() + ", Level " + eedi.getSeverity() + ", State " + eedi.getState() + ":");

                    if( eedi.getServerName() != null) {
                        logger.severe(", Server " + eedi.getServerName());
                        firstOnLine = false;
                    }
                    if(eedi.getProcedureName() != null) {
                        logger.severe((firstOnLine ? "" : ", ") + "Procedure " + eedi.getProcedureName());
                        firstOnLine = false;
                    }
                    logger.severe((firstOnLine ? "" : ", ") + "Line " + eedi.getLineNumber() + ", Status " + eedi.getStatus() + ", TranState " + eedi.getTranState() + ":");
                }
                // Now print the error or warning
                logger.severe(sqe.getMessage());
                isFatalError = isFatalError || (sqe.getErrorCode() != 2007);
            }
            else {
                */
                if ( "24000".equals(sqe.getSQLState()) ){} /* FreeTDS: Message:  The executeQuery method must return a result set. SQLState: 24000 */
                else if ( sqe.getErrorCode() == 0 && sqe.getSQLState() == null ) System.out.println(sqe.getMessage ());
                else {
                    logger.severe("Message:  " + sqe.getMessage());
                    logger.severe("SQLState: " + sqe.getSQLState());
                    System.out.println("Message:  " + sqe.getMessage());
                    System.out.println("SQLState: " + sqe.getSQLState());
					isFatalError = isFatalError || (sqe.getErrorCode() != 0 &&
													sqe.getErrorCode() != 2007 &&   // 
													sqe.getErrorCode() != 5701 );   // changed database context
                }
            //}
            sqe = sqe.getNextException();
        }
        
        return isFatalError;
    }

    /**
     * Display resultset.
     * @param rs resultset to display
     * @param cmdopt command line arguments
     * @throws SQLException, JbsqlException
     */
    public static void displayResultSet (final ResultSet rs, final CmdOpt cmdopt) throws SQLException, JbsqlException {
        int i;

        // Get the ResultSetMetaData.  This will be used for the column headings
        ResultSetMetaData rsmd = rs.getMetaData ();

        //  Get the number of columns in the result set
        int numCols = rsmd.getColumnCount ();

        // Display column headings
        /*for ( i = 1; i <= numCols; i++ ) {
            if ( i > 1 ) System.out.append(cmdopt.getColumnSeparator());
            System.out.append(rsmd.getColumnLabel(i));
        }
        System.out.append(cmdopt.getRowSeparator());*/

        String s = "";
        // Display data, fetching until end of the result set
        while ( rs.next() ) {
            // Loop through each column, getting the column data and displaying
            for ( i = 1; i <= numCols; i++ ) {
                if ( i > 1 ) s += cmdopt.getColumnSeparator();

                String foobar = rs.getString(i);
                if ( rs.wasNull() ) s += "NULL";
                else s += foobar;
            }
            s += cmdopt.getRowSeparator();
        }

        PrintStream outputStream;
        try { outputStream = (cmdopt.getOutputFileName() == null ? System.out : new PrintStream(new FileOutputStream(cmdopt.getOutputFileName(), true))); }
        catch (FileNotFoundException ex) { throw new JbsqlException("FNF_01", cmdopt.getOutputFileName(), ex); }

        outputStream.print(s);
        outputStream.flush();

        if ( cmdopt.getOutputFileName() != null ) outputStream.close();

        logger.info(s);
    }



    /**
     * Formatted JDBC class string.
     * @return String
     */
    public abstract String getJdbcClass();

    /**
     * Formatted driver string.
     * @return String
     */
    public abstract String getJdbcDriver();

    /**
     * Formatted connection URL.
     * @param servername Server name
     * @param db Database name
     * @return String
     */
    public abstract String getConnectionString(final String servername, final String db);

    /**
     * Driver-dependent table statement.
     * @param isTemp temporary table flag
     * @param checkExisting check existing clause
     * @param tablename table name
     * @param col_array columns array {"name", "nullable", "collation"}
     * @return String
     */
    public abstract String getCreateTableStmt(boolean isTemp, boolean checkExisting, String tablename, String col_array[][]);

    /**
     * Driver-dependent temporary table symbol.
     * @return String
     */
    public abstract String tempTableSymbol();

    /**
     * Driver-dependent temporary current date statement.
     * @return String
     */
    public abstract String getDateStmt();

    /**
     * Driver-dependent commands terminator.
     * @return String
     */
    public abstract String getCmdTerminator();

    /**
     * Initialize connection: run driver-specific code.
     * @param conn Connection
     * @throws NullPointerException
     * @throws SQLException
     */
    public abstract void initConnection(final Connection conn) throws NullPointerException, SQLException;

    /**
     * Return "code before" statement
     * @return String
     */
    public abstract String getCodeBeforeStart();

    /**
     * Return "code after" statement
     * @return String
     */
    public abstract String getCodeAfterStart();

    /**
     * Return "code end" statement
     * @return String
     */
    public abstract String getCodeEnd();

    /**
     * verifies if string is a reserved token
     * @return String
     */
    public abstract int getReservedToken(final String str);

    /**
     * returns "collation" (MSSQL specifics)
     * @return String
     */
    public String getCollationDefaultStmt() {
        return "";
    }

    public abstract long getTranCount(final Connection conn);
}
